﻿using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Text;
using System.Web;
using System.Web.UI;
using System.Web.UI.WebControls;
using System.Web.UI.HtmlControls;
using System.Xml;
using System.IO;

namespace JTW.Web.UI.WebControls
{
    [DefaultProperty("XmlInputFile")]
    [ToolboxData("<{0}:BootstrapNavigation runat=server></{0}:BootstrapNavigation>")]
    public class BootstrapNavigation : WebControl
    {
        /// <summary>
        /// Holds the location of the XML input file.  Most likely a URL.
        /// </summary>
        [Bindable(true)]
        [Category("Navigation Options")]
        [DefaultValue("")]
        [Description("Holds the name of the XML input file")]
        [Localizable(true)]
        public string XmlInputFile
        {
            get
            {
                String s = (String)ViewState["XmlInputFile"];
                return ((s == null) ? null : s);
            }

            set
            {
                ViewState["XmlInputFile"] = value;
            }
        }

        /// <summary>
        /// This holds parameters for all NavigationItem elements which have them
        /// </summary>
        [Bindable(true)]
        [Category("Navigation Options")]
        [DefaultValue("")]
        [Description("Holds parameters and values")]
        [Localizable(true)]
        public Dictionary<string, Dictionary<string, string>> NavigationParameters
        {
            get
            {
                Dictionary<string, Dictionary<string, string>> s = (Dictionary<string, Dictionary<string, string>>)ViewState["NavigationParameters"];
                if (s == null)
                {
                    s = new Dictionary<string, Dictionary<string, string>>();  // initialize it if attempted use with no instance
                    ViewState["NavigationParameters"] = s;
                }
                return s;
            }

            set
            {
                ViewState["NavigationParameters"] = value;
            }
        }

        /// <summary>
        /// Determines if administration NavigationItem elements should be made visible.  Defaults to false.
        /// </summary>
        [Bindable(true)]
        [Category("Navigation Options")]
        [DefaultValue("")]
        [Description("Should Administration items be shown?")]
        [Localizable(true)]
        public bool IsAdministrator
        {
            get
            {
                bool? s = (bool?)ViewState["IsAdministrator"];
                if (s == null)
                {
                    s = false;  // default to false if not set
                    ViewState["IsAdministrator"] = (bool)s;
                }
                return (bool)s;
            }

            set
            {
                ViewState["IsAdministrator"] = Convert.ToBoolean(value);
            }
        }

        /// <summary>
        /// Determines if developer NavigationItem elements should be made visible.  Defaults to false.
        /// </summary>
        [Bindable(true)]
        [Category("Navigation Options")]
        [DefaultValue("")]
        [Description("Should Developer items be shown?")]
        [Localizable(true)]
        public bool IsDeveloper
        {
            get
            {
                bool? s = (bool?)ViewState["IsDeveloper"];
                if (s == null)
                {
                    s = false;// default to false if not set
                    ViewState["IsDeveloper"] = (bool)s;
                }
                return (bool)s;
            }

            set
            {
                ViewState["IsDeveloper"] = Convert.ToBoolean(value);
            }
        }

        /// <summary>
        /// Actions to take when client clicks navigation items.
        /// OnClientClickActions is normally JavaScript code.
        /// </summary>
        [Bindable(true)]
        [Category("Navigation Options")]
        [DefaultValue("")]
        [Description("Actions to take when client clicks navigation items")]
        [Localizable(true)]
        public Dictionary<string, string> OnClientClickActions
        {
            get
            {
                Dictionary<string, string> s = (Dictionary<string, string>)ViewState["OnClientClickActions"];
                if (s == null)
                {
                    s = new Dictionary<string, string>();  // initialize it if attempted use with no instance
                    ViewState["OnClientClickActions"] = s;
                }
                return s;
            }

            set
            {
                ViewState["OnClientClickActions"] = value;
            }
        }

        /// <summary>
        /// Overriding the Render method to correct span issue
        /// </summary>
        /// <param name="writer"></param>
        protected override void Render(HtmlTextWriter writer)
        {
            // this removes the <span> element that controls which inherit from WebControl get
            RenderContents(writer);
        }

        protected override void RenderContents(HtmlTextWriter output)
        {
            // allow for relative URLs
            if (XmlInputFile.Trim().StartsWith("~"))
            {
                XmlInputFile = XmlInputFile.Trim().Replace("~", HttpContext.Current.Request.Url.Scheme + "://" + HttpContext.Current.Request.Url.Authority);
            }

            // load the XML file
            XmlDocument navDocument = new XmlDocument();
            navDocument.Load(XmlInputFile);

            // construct navigation from loaded XML
            XmlNodeList navItems = navDocument.SelectNodes("/NavigationItems/NavigationItem");

            // write navigation to client
            output.Write(ConstructNavigationFromXml(navItems));
        }

        /// <summary>
        /// Constructs navigation. Fires recursively if sub-menus exist.
        /// </summary>
        /// <param name="navItemsInput"></param>
        /// <returns></returns>
        private string ConstructNavigationFromXml(XmlNodeList navItems)
        {
            string navigationItems = string.Empty; // this is the return value of the method

            bool isAdmin = IsAdministrator;
            bool isDeveloper = IsDeveloper;

            // iterate through each NavigationItem node found in XML file
            foreach (XmlNode navItem in navItems)
            {
                // get attributes and elements from XML for the current navigation item
                string navItemId = (navItem.Attributes["id"] != null) ? navItem.Attributes["id"].Value : "NoIdProvided"; // add an 'id' if none provided
                string navItemAdmin = (navItem.Attributes["admin"] != null) ? navItem.Attributes["admin"].Value : "false"; // if no 'admin' attribute, default to false
                string navItemDev = (navItem.Attributes["development"] != null) ? navItem.Attributes["development"].Value : "false"; // if no 'development' attribute, default to false
                string navItemHidden = (navItem.Attributes["hidden"] != null) ? navItem.Attributes["hidden"].Value : "false"; // if no 'hidden' attribute, default to false
                string navItemName = (navItem.SelectSingleNode("Name") != null && !String.IsNullOrEmpty(navItem.SelectSingleNode("Name").InnerText)) ? navItem.SelectSingleNode("Name").InnerText : null; // if no name provided, default to null
                string navItemUrl = (navItem.SelectSingleNode("Url") != null && !String.IsNullOrEmpty(navItem.SelectSingleNode("Url").InnerText)) ? navItem.SelectSingleNode("Url").InnerText : null; // if no urk provided, default to null
                string navItemIcon = (navItem.SelectSingleNode("IconClass") != null && !String.IsNullOrEmpty(navItem.SelectSingleNode("IconClass").InnerText)) ? navItem.SelectSingleNode("IconClass").InnerText : null; // if no icon provided, default to null
                string navItemCss = (navItem.SelectSingleNode("CssClass") != null && !String.IsNullOrEmpty(navItem.SelectSingleNode("CssClass").InnerText)) ? navItem.SelectSingleNode("CssClass").InnerText : null; // if no css class provided, default to null
                string navItemDropdown = (navItem.Attributes["dropdown"] != null) ? navItem.Attributes["dropdown"].Value : "false"; // if no 'dropdown' attribute, default to false

                // write parameters and parameter values to the URL of the current navigation item
                if (NavigationParameters != null && NavigationParameters.ContainsKey(navItemId))
                {
                    navItemUrl += "?";

                    Dictionary<string, string> navParams = NavigationParameters[navItemId];

                    if (navParams != null)
                    {
                        foreach (var pair in navParams)
                        {
                            navItemUrl += pair.Key + "=" + pair.Value + "&";
                        }
                    }

                    navItemUrl = navItemUrl.Remove(navItemUrl.Length - 1); // removes the trailing '&' or the '?' if navParmas was empty
                }

                // create the li element from current XML node to be added to page
                if (navItemHidden != "true" && (navItemAdmin == "false" || (navItemAdmin == "true" && isAdmin == true)))
                {
                    if (navItemDev == "false" || (navItemDev == "true" && isDeveloper == true))
                    {
                        HtmlGenericControl li = new HtmlGenericControl("li");
                        HtmlGenericControl anchor = new HtmlGenericControl("a");
                        HtmlGenericControl i = new HtmlGenericControl("i");
                        HtmlGenericControl span = new HtmlGenericControl("span");
                        HtmlGenericControl b = new HtmlGenericControl("b");

                        if (navItemCss != null) li.Attributes.Add("class", navItemCss); // adds class to li element

                        if (navItemName != null) span.InnerHtml = "&nbsp;" + navItemName + "&nbsp;"; // adds displayed text to span element

                        if (navItemIcon != null) i.Attributes.Add("class", navItemIcon); // adds class to icon element

                        if (navItemUrl != null) anchor.Attributes.Add("href", navItemUrl); // add url to anchor element

                        // based on what is available in XML, construct an adequate li element
                        if (navItemUrl != null)
                        {
                            if (OnClientClickActions != null && OnClientClickActions.ContainsKey(navItemId)) anchor.Attributes.Add("onclick", System.Web.HttpUtility.HtmlDecode(OnClientClickActions[navItemId].Trim()));

                            if (navItemIcon != null) anchor.Controls.Add(i);

                            if (navItemName != null) anchor.Controls.Add(span);

                            li.Controls.Add(anchor);

                            // if the navigation item has sub-menus
                            if (navItemDropdown != "false")
                            {
                                anchor.Attributes.Add("class", "dropdown-toggle");
                                anchor.Attributes.Add("data-toggle", "dropdown");
                                b.Attributes.Add("class", "caret");
                                anchor.Controls.Add(b);
                                HtmlGenericControl ul = new HtmlGenericControl("ul");
                                ul.Attributes.Add("class", "dropdown-menu");

                                // recursively call this method for sub-menus
                                ul.InnerHtml = ConstructNavigationFromXml(navItem.Clone().SelectNodes("/NavigationItems/NavigationItem"));
                                li.Controls.Add(ul);
                            }
                        }

                        else if (navItemUrl == null)
                        {
                            if (navItemIcon != null) li.Controls.Add(i);

                            if (navItemName != null) li.Controls.Add(span);
                        }

                        StringWriter sw = new StringWriter();
                        HtmlTextWriter hw = new HtmlTextWriter(sw);
                        li.RenderControl(hw);
                        navigationItems += sw.ToString();
                    }
                }
            }

            return navigationItems;
        }
    }
}
